package com.crawler.search.index.querier;

import com.alibaba.fastjson.JSONObject;
import com.crawler.search.constant.BoolType;
import com.crawler.search.constant.CommonColumn;
import com.crawler.search.constant.QuerierMapper;
import com.crawler.search.index.entity.QueryParam;
import com.crawler.search.index.keyword.KeywordType;
import com.crawler.search.index.keyword.KeywordUtil;
import com.crawler.search.index.keyword.Node;
import com.crawler.search.utils.BoolQueryUtil;
import com.google.common.base.CaseFormat;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.apache.commons.lang3.StringUtils;
import org.elasticsearch.index.query.*;
import org.springframework.util.CollectionUtils;

import java.util.HashMap;
import java.util.List;
import java.util.Map;


/**
 * ES数据查询实体
 *
 * @author zhangyonghui
 * @Class Name ESService
 * @Create In 下午3:50:26
 */
@Data
@NoArgsConstructor
public class MultiMatchQuerier {

    //查询多字段时，key为ES中字段名称，value为权重
    protected Map<String, Float> fieldBoostMap = new HashMap<String, Float>();

    //查询多字段时，key为查询字段，value为ES中字段名称
    protected Map<String, String> fieldMap = new HashMap<String, String>();

    //查询多字段时，key为查询字段，value为ES中字段名称
    protected Map<String, String> shouldMap = new HashMap<String, String>();

    //查询多字段时，key为查询字段，value为ES中字段名称
    protected Map<String, String> segMap = new HashMap<String, String>();

    protected Map<String, JSONObject> rangeMap = new HashMap<String, JSONObject>();

    //多匹配查询的类型
    protected MultiMatchQueryBuilder.Type type = MultiMatchQueryBuilder.Type.MOST_FIELDS;

    //查询参数
    protected String search;

    public MultiMatchQuerier(QueryParam param) {
        this.fieldBoostMap = param.getFieldBoostMap();
        this.fieldMap = param.getFiledMap();
        this.shouldMap = param.getShouldMap();
        this.segMap = param.getSegMap();
        this.rangeMap = param.getRangeMap();
        this.search = param.getSearch();
    }


    /**
     * mutli_match 查询
     *
     * @return
     */
    public QueryBuilder buildQuery() {
        JSONObject json = JSONObject.parseObject(search);
        if (json.containsKey(CommonColumn.KEYWORD)) {
            String keyword = json.getString(CommonColumn.KEYWORD);
            MultiMatchQueryBuilder query = QueryBuilders.multiMatchQuery(keyword)
                    .slop(0)
                    .type(type)
                    .minimumShouldMatch("100%")
                    .operator(Operator.OR);
            if (json.containsKey(CommonColumn.FIELD)) {
                query.field(json.getString(CommonColumn.FIELD), 5L);
            } else {
                for (Map.Entry<String, Float> entry : fieldBoostMap.entrySet()) {
                    query.field(entry.getKey(), entry.getValue());
                }
            }
            return query;
        } else {
            BoolQueryBuilder query = new BoolQueryBuilder();
            query.must(QueryBuilders.matchAllQuery());
            return query;
        }
    }

    /**
     * mutli_match 查询
     *
     * @return
     */
    public QueryBuilder buildQuery(JSONObject json) {
        if (json.containsKey(CommonColumn.KEYWORD)) {
            String keyword = json.getString(CommonColumn.KEYWORD);
            MultiMatchQueryBuilder query = QueryBuilders.multiMatchQuery(keyword)
                    .slop(0)
                    .type(type)
                    .minimumShouldMatch("100%")
                    .operator(Operator.OR);
            if (json.containsKey(CommonColumn.FIELD)) {
                query.field(json.getString(CommonColumn.FIELD), 5L);
            } else {
                for (Map.Entry<String, Float> entry : fieldBoostMap.entrySet()) {
                    query.field(entry.getKey(), entry.getValue());
                }
            }
            return query;
        } else {
            BoolQueryBuilder query = new BoolQueryBuilder();
            query.must(QueryBuilders.matchAllQuery());
            return query;
        }
    }

    /**
     * bool查询嵌套
     *
     */
    public QueryBuilder buildBoolQuery() {
        JSONObject obj = JSONObject.parseObject(search);
        BoolQueryBuilder query = new BoolQueryBuilder();

        BoolQueryUtil.buildRange(query, rangeMap);//排序

        BoolQueryUtil.addMust(obj, query, fieldMap, CaseFormat.LOWER_UNDERSCORE);

        BoolQueryUtil.addShould(obj, query, shouldMap, CaseFormat.LOWER_UNDERSCORE);

        BoolQueryUtil.addSegMust(obj, query, segMap, CaseFormat.LOWER_UNDERSCORE);

        if (obj.containsKey(CommonColumn.KEYWORD)) {
            String keyword = obj.getString(CommonColumn.KEYWORD);
            if (StringUtils.isNotEmpty(keyword)) {
                if (keyword.contains(BoolType.AND.getValue())) {
                    String[] split = keyword.split(BoolType.AND.getValue());
                    for (String word : split) {
                        obj.put(CommonColumn.KEYWORD, word);
                        query.must(buildQuery());
                    }
                } else if (keyword.contains(BoolType.OR.getValue())) {
                    String[] split = keyword.split(BoolType.OR.getValue());
                    for (String word : split) {
                        obj.put(CommonColumn.KEYWORD, word);
                        query.should(buildQuery());
                    }
                    query.minimumShouldMatch(1);
                } else if (keyword.contains(BoolType.NOT.getValue())) {
                    String[] split = keyword.split(BoolType.NOT.getValue());
                    for (String word : split) {
                        obj.put(CommonColumn.KEYWORD, word);
                        query.should(buildQuery());
                    }
                } else {
                    query.must(buildQuery());
                }
                return query;
            }
        }else if(CollectionUtils.isEmpty(query.must()) ){
            return null;
        }
        return query;
    }


    /**
     * bool高级查询嵌套
     *
     * @param
     */
    public QueryBuilder buildSeniorBoolQuery() {
        JSONObject obj = JSONObject.parseObject(search);
        BoolQueryBuilder query = new BoolQueryBuilder();
        BoolQueryUtil.buildRange(query, rangeMap);//排序
        BoolQueryUtil.addShould(obj, query, shouldMap, CaseFormat.LOWER_UNDERSCORE);
        BoolQueryUtil.addMust(obj, query, fieldMap, CaseFormat.LOWER_UNDERSCORE);
        BoolQueryUtil.addSegMust(obj, query, segMap, CaseFormat.LOWER_UNDERSCORE);

        if (obj.containsKey(CommonColumn.KEYWORD)) {
            String keyword = obj.getString(CommonColumn.KEYWORD);
            if (StringUtils.isNotEmpty(keyword)) {
                List<Node> tree = KeywordUtil.getTree(keyword);
                buildRangeNode(tree,query);
                buildKeywordNode(tree,query);
                for (Node node : tree) {
                    if(StringUtils.isNotEmpty(node.getFiled())){
                        if(node.getFiled().contains("begin")
                            ||node.getFiled().contains("end")
                            ||node.getFiled().contains("all_text")
                        ){
                            continue;
                        }
                    }
                    String type = node.getType();
                    String word = node.getWord();
                    String field = node.getFiled();
                    obj.put(CommonColumn.KEYWORD, word);
                    if (StringUtils.isNotEmpty(field)) {
                        obj.put(CommonColumn.FIELD, field);
                    }
                    if (KeywordType.must.toString().equals(type)) {
                        query.must(buildQuery(obj));
                    } else if (KeywordType.should.toString().equals(type)) {
                        query.should(buildQuery(obj));
                        query.minimumShouldMatch(1);
                    } else if (KeywordType.mustNot.toString().equals(type)) {
                        query.mustNot(buildQuery(obj));
                    } else {
                        if (StringUtils.isNotEmpty(word)) {
                            query.must(buildQuery(obj));
                        }
                    }
                }
                return query;
            }
        } else {
            if (CollectionUtils.isEmpty(query.must())) {
                return null;
            }
        }
        return query;
    }

    public void buildRangeNode(List<Node> tree, BoolQueryBuilder boolQuery) {
        String begin="";
        String end="";
        String type = "";
        for (int i = 0; i < tree.size(); i++) {
            Node node = tree.get(i);
            if(StringUtils.isNotEmpty(node.getFiled())){
                if (node.getFiled().startsWith("beginTime")) {
                    type = node.getType();
                    String searchValue = node.getWord();
                    //日期筛选
                    if(StringUtils.isNotEmpty(searchValue)){
                        begin = searchValue.split("\\|")[0];
                        if(searchValue.split("\\|").length>1){
                            end=searchValue.split("\\|")[1];
                        }
                    }
                }
            }
        }
        RangeQueryBuilder format = QueryBuilders.rangeQuery("pubDate").format(CommonColumn.FORMAT_DAY);
        if(StringUtils.isNotEmpty(begin)){
            format.gte(begin);
        }
        if(StringUtils.isNotEmpty(end)){
            format.lte(end);
        }
        if(type.equals(KeywordType.should.name())){
            boolQuery.should(format);
        }else if(type.equals(KeywordType.must.name())){
            boolQuery.must(format);
        }else if(type.equals(KeywordType.mustNot.name())){
            boolQuery.mustNot(format);
        }

    }

    public void buildKeywordNode(List<Node> tree, BoolQueryBuilder boolQuery) {
        String type = "";
        for (int i = 0; i < tree.size(); i++) {
            Node node = tree.get(i);
            if(StringUtils.isNotEmpty(node.getFiled())){
                if (node.getFiled().equals(QuerierMapper.ALL_TEXT)) {
                    String word = node.getWord();
                    type = node.getType();
                    if(StringUtils.isNotEmpty(word)){
                        MultiMatchQueryBuilder query = QueryBuilders.multiMatchQuery(word)
                                .slop(0)
                                .type(MultiMatchQueryBuilder.Type.MOST_FIELDS)
                                .minimumShouldMatch("100%")
                                .operator(Operator.OR);
                        for (Map.Entry<String, Float> entry : fieldBoostMap.entrySet()) {
                            query.field(entry.getKey(), entry.getValue());
                        }
                        if(type.equals(KeywordType.should.name())){
                            boolQuery.should(query);
                        }else if(type.equals(KeywordType.must.name())){
                            boolQuery.must(query);
                        }else if(type.equals(KeywordType.mustNot.name())){
                            boolQuery.mustNot(query);
                        }
                    }
                }
            }
        }
    }

}
